#include <functional>

#include "udp.hpp"
#include "sdp.hpp"
#include "sys_var.hpp"
#include "at.hpp"
#include "dial.hpp"

udp::udp( io_type type , filterList * l )
: filterIO( type , l )
{
    p_udp = std::move( std::unique_ptr< CUVUDPClient>( new CUVUDPClient( "" , -1 ) ) );
	m_index= 0;

	std::shared_ptr< CSysVar > ptvar = GetOrCreateSysVar();
	if( ptvar ){
		bool en = false;
		ptvar->GetValue( "/net/sdpen" , en );

		m_sdp_en = en;
	}
}

udp::~udp()
{
    m_heart_timer.Stop();
}

void udp::Index( int i )
{
	m_index = i;
	init_heart();
	init_reg();
}

void udp::init_heart()
{
	std::shared_ptr< CSysVar > ptvar = GetOrCreateSysVar();

	if( ptvar ){
		bool en ;
		long intval;
		std::stringstream ss;
		std::string  str;

		ss << m_index.load();
		str = "/net/socket/sock" + ss.str() + "/heart/";
		ptvar->GetValue( str + "enable" , en );
		m_heart_en = en;
		ptvar->GetValue( str + "heart_send_intval" , intval );
		m_heart_intval = intval;
		m_heart_timer( intval * 1000 );
		ptvar->GetValue( str + "hex" , en );
		m_heart_hex = en;
		ptvar->GetValue( str + "data" , m_heart_data );
		if( en ){
			uint8_t *d = ( uint8_t *)malloc( m_reg_data.length() );
			if( d ){
				try{
					size_t len = str2hex( m_heart_data , d );
					m_heart_data_hex.data( d , len );
					free( d );
				}catch( ... ){
					__show_line_1( "%s%s%s", TRED , "Initialize heart packet fail." , TNORMAL );
				}
			}else{
				__show_line_1( "%s%s%s", TRED , "Allocate memory fail." , TNORMAL );
			}		
		}
		
		m_heart_timer.SetOwner([ this ]{
			if( !IsInSerialAT() ){
				if( m_heart_hex == true )
					p_udp->Send( ( char *)m_heart_data_hex.data() , m_heart_data_hex.size() );
				else
					p_udp->Send( m_heart_data.c_str() , m_heart_data.length() );
			}

			m_heart_timer.Start( -1 , true );
		});

		if( m_heart_en == true   )
			m_heart_timer.Start( -1, true );
	}else{
		std::cout << "Can not initialize heart package for sysvar fail." << std::endl;
		abort();
	}
}

void udp::init_reg()
{
	std::shared_ptr< CSysVar > ptvar = GetOrCreateSysVar();

	if( ptvar ){
		bool en;
		std::string  type, send_type;
		std::stringstream ss;
		std::string  str;
		ss << m_index.load();
		str = "/net/socket/sock" + ss.str() + "/reg/";

		ptvar->GetValue( str + "enable" , en );
		ptvar->GetValue( str + "data_type" , type );
		ptvar->GetValue( str + "type" , send_type );
		ptvar->GetValue( str + "hex" , en );
		m_reg_hex = en;
		if( type == "USER" ){
			ptvar->GetValue( str + "data" , m_reg_data );
			if( m_reg_hex.load() ){
				uint8_t *d = ( uint8_t *)malloc( m_reg_data.length() );
				if( d ){
					try{
						size_t len = str2hex( m_reg_data , d );
						m_reg_data_hex.data( d , len );
						free( d );
					}catch( ... ){
						__show_line_1( "%s%s%s", TRED , "Initialize register packet fail." , TNORMAL );
					}
				}else{
					__show_line_1( "%s%s%s", TRED , "Allocate memory fail." , TNORMAL );
				}
			}
		}
		else if( type =="ICCID"){
			m_reg_data = ec20dial::GetICCID();
		}else if( type == "IMEI" ){
			m_reg_data = ec20dial::GetIMEI();
		}else if( type == "CLOUD"){
			// TODO , 添加和SignalCloud 互动的代码
		}else{
			m_reg_data = ec20dial::GetIMEI();
		}
	}else{
		std::cout << "Can not initialize register package for sysvar fail." << std::endl;
		abort();
	}
}

bool udp::on_forward( size_t len , const char * data , err_code &err , size_t& rlen , char ** rd )
{
	if( m_heart_en == true ){
		m_heart_timer.Stop();
	}

  	bool ret = false;
	CUVUDPClient::err_code e =  CUVUDPClient::OK;
	std::string str( data );
	if( IsInSerialAT() || str.find( "#AT") != std::string::npos ){
		*rd = ( char *)data;
		rlen = len;
		m_heart_timer.Reset();
		return false;
	}

	if( m_sdp_en == true ){ // 如果开启了SDP功能，则解析SDP数据包，摘取实际数据执行发送
		sdp __sdp;  size_t __len = 0; void * __data = nullptr;
		if( __sdp.Unpack( data , len ) && __sdp.socket() == m_index.load() ){
			__len = __sdp.GetData( &__data );
			if( m_reg_send_type == "DATA"){
				if( m_reg_hex == true )
					p_udp->Send( ( char *)m_reg_data_hex.data() , m_reg_data_hex.size() );
				else
					p_udp->Send( m_reg_data.c_str() , m_reg_data.length() );
			}
			if( __data ) p_udp->Send( ( const char *)__data , __len );
		}

		if( __data ) free( __data );

	}else{ // 如果没有开启SDP功能则直接发送数据
		if( m_reg_send_type == "DATA"){
			if( m_reg_hex == true )
				p_udp->Send( ( char *)m_reg_data_hex.data() , m_reg_data_hex.size() );
			else
				p_udp->Send( m_reg_data.c_str() , m_reg_data.length() );
		}
		
  		e = p_udp->Send( data , len );
	}
	if( e == CUVUDPClient::OK ){
		rlen = len;
		* rd = ( char *)data;
		ret = false;
	}else{
		ret = false;
	}

	if( m_heart_en == true ){
		m_heart_timer.Start( -1 , true );
	}
   	return ret;
}

bool udp::on_backward( size_t len , const char * data , err_code &err , size_t& rlen , char ** rd  )
{
	if( data == nullptr || len == 0 )
        return false;

	if( IsInSerialAT() ){
		*rd = ( char *)data;
		rlen = len;
		return false;
	}
	if( m_sdp_en ){
		sdp __sdp;
		__sdp.Net( (sdp::net)( 0x60 + m_index.load() ) , data , len );
		void * __data = nullptr; size_t __len;

		__len = __sdp.GetPackRaw( &__data );
		if( __data ){
			*rd = ( char *)__data;
			rlen = __len;
		}
	}else{
		rlen = len;
		*rd = ( char *)data;
	}
    return true;
}

void udp::connect()
{
	if( !p_udp ){ throw ERR_UDP_NOT_INIT; }

	p_udp->cb_recv = [ this ]( size_t s , const char * buff ){
		if( buff && s > 0 )
			OnRecv( s , buff , OK );
		else
			OnRecv( 0 , nullptr , ERR_UDP_RECV_NULL );
	};

	p_udp->cb_conn = [ this ](){ };

	p_udp->cb_send = [ this ]( size_t s ){ };

	p_udp->cb_close = [ this ](){ };
	p_udp->connect();
	if( m_reg_send_type == "LINK" || m_reg_send_type == "BOTH" ){
		if( m_reg_hex == true )
			p_udp->Send( ( char *)m_reg_data_hex.data() , m_reg_data_hex.size() );
		else
			p_udp->Send( m_reg_data.c_str() , m_reg_data.length() );
	}
}

void udp::connect( const std::string& svr , int port )
{
	if( !p_udp ){
		try{
			p_udp = std::move( std::unique_ptr< CUVUDPClient>( new CUVUDPClient( svr , port )));
		}
		catch( std::bad_alloc& e ){
			std::cerr << TRED << "Can not alloc UDP Object" << TNORMAL << std::endl;
			abort();
		}
	}
	p_udp->cb_recv = [ this ]( size_t s , const char * buff ){
		if( buff && s > 0 )
			OnRecv( s , buff , OK );
		else
			OnRecv( 0 , nullptr , ERR_UDP_RECV_NULL );
	};

	p_udp->cb_conn = [ this ](){ };

	p_udp->cb_send = [ this ]( size_t s ){ };

	p_udp->cb_close = [ this ](){ };
	
   	p_udp->connect( svr , port );
	if( m_reg_send_type == "LINK" || m_reg_send_type == "BOTH" ){
		if( m_reg_hex == true )
			p_udp->Send( ( char *)m_reg_data_hex.data() , m_reg_data_hex.size() );
		else
			p_udp->Send( m_reg_data.c_str() , m_reg_data.length() );
	}
}
